package services

import (
	"errors"
	"time"

	"github.com/EDDYCJY/go-gin-example/pkg/openai/aijieyun/pkg/services/avpc"
	serverError "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/vpc"
	log "github.com/sirupsen/logrus"
)

type EIPAllocator struct {
	Client *avpc.Client
	Work   Worker
}

func NewEIPAllocator(client *avpc.Client, work Worker) *EIPAllocator {
	return &EIPAllocator{
		Client: client,
		Work:   work,
	}
}

// DescribeIsp 查询公网IP线路列表中的第一个元素的IP
func (e *EIPAllocator) DescribeIsp() (string, error) {
	rpcRequest := &requests.RpcRequest{}
	rpcRequest.InitWithApiInfo("Ecs", "2014-05-26", "DescribeIsp", "ecs", "openAPI")
	describeIsp, err := e.Client.DescribeIsp(rpcRequest)
	if err != nil {
		return "", err
	}

	//取出第一个公网的ID
	id, err := describeIsp.FirstIspId()
	if err != nil {
		return "", err
	}

	return id, nil
}

// AllocateEipAddress 申请弹性公网IP
func (e *EIPAllocator) AllocateEipAddress(ispID string) (*vpc.AllocateEipAddressResponse, error) {
	req := vpc.CreateAllocateEipAddressRequest()
	req.ISP = ispID
	req.Bandwidth = "1" // 1M带宽
	resp, err := e.Client.AllocateEipAddress(req)
	log.Infof("AssociateEipAddress resp: %v", resp)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// AssociateEipAddress 给机器分配网络
func (e *EIPAllocator) AssociateEipAddress(allocationId string) error {
	assoReq := vpc.CreateAssociateEipAddressRequest()
	assoReq.AllocationId = allocationId
	assoReq.InstanceId = e.Work.GetID()
	var err error
	//这个时间经测试，需要等待20秒是一个比较不错的值
	time.Sleep(20 * time.Second)
	for i := 7; i > 0; i-- {

		_, err = e.Client.AssociateEipAddress(assoReq)
		if err != nil {
			log.Debugf("分配第%d次弹性网络地址失败%v", 6-i, err.Error())
			var customErr *serverError.ServerError
			if errors.As(err, &customErr) {
				if customErr.ErrorCode() == "InvalidAssociation.Duplicated" {
					//如果已经成功绑定，不需要再次绑定
					err = nil
					break
				}
				if customErr.ErrorCode() == "IncorrectInstanceStatus" {
					//如果机器状态不对，需要重新创建机器
					//这里后续结合情况修改

				}
				//如果机器状态不对，需要重新创建机器
				time.Sleep(5 * time.Second)
			}
		} else {
			break
		}

	}

	return err
}

func (e *EIPAllocator) AllocateAndAssociateEipAddress() error {
	//查询公网IP线路列表中的第一个元素的IP
	ispID, err := e.DescribeIsp()
	if err != nil {
		log.Infof("获取公网线路IP失败:%v", err)
		return err
	}

	//申请弹性公网IP
	eipResponse, err := e.AllocateEipAddress(ispID)
	if err != nil {
		log.Infof("申请弹性公网IP失败:%v", err)

		return err
	}

	//给机器分配网络
	err = e.AssociateEipAddress(eipResponse.AllocationId)
	if err != nil {
		log.Infof("给机器分配网络失败 err:%v", err.Error())
		return err
	}

	return nil
}
